import datetime
import json
import logging

from flask import abort, flash, g, redirect
from flask_appbuilder.actions import action
from flask_appbuilder.models.sqla.interface import SQLAInterface
from flask_babel import gettext as __
from sqlalchemy import or_

# Import base API functionalities
# 导入基础API功能
from myapp.apis.base import expose
# Import workflow-related API functions
# 导入工作流相关的API功能
from myapp.apis.workflow import create_model_if_finished, sync_job_status
# Import application instance, appbuilder, and database instance
# 导入应用实例、appbuilder和数据库实例
from myapp.app import app, appbuilder, db
# Import common error codes
# 导入通用错误码
from myapp.const.error import CommonErrorCode
# Import business error and response wrapper
# 导入业务错误和响应包装器
from myapp.const.response import BizError, wrap_response
# Import Pipeline and Workflow models
# 导入Pipeline和Workflow模型
from myapp.models.model_job import Pipeline, Workflow
# Import push message utility
# 导入推送消息工具
from myapp.project import push_message
# Import Kubernetes client
# 导入Kubernetes客户端
from myapp.third.k8s import py_k8s
# Import ModelArts job utility
# 导入ModelArts作业工具
from myapp.third.modelarts.training_job import ModelArtsJob

# Import restart training function from pipeline business logic
# 从管道业务逻辑导入重启训练功能
from ..biz.pipeline.workflow import restart_training
# Import error code utility for continuing training
# 导入用于继续训练的错误码工具
from ..utils.error_code import can_continuing_training
# Import base view classes for deletion mixin, custom filter, and model view
# 导入基础视图类，用于删除混合、自定义过滤器和模型视图
from .base import (
    DeleteMixin,
    MyappFilter,
    MyappModelView,
)
# Import base API class
# 导入基础API类
from .baseApi import MyappModelRestApi


# Get application configuration
# 获取应用配置
conf = app.config


# Custom filter for CRD (Custom Resource Definition) objects
# CRD（自定义资源定义）对象的自定义过滤器
class CRD_Filter(MyappFilter):
    # Apply the filter to the query
    # 将过滤器应用于查询
    # @pysnooper.snoop()
    def apply(self, query, func):
        # Get user roles and convert to lowercase
        # 获取用户角色并转换为小写
        user_roles = [role.name.lower() for role in list(self.get_user_roles())]
        # If user is an admin, order by creation time in descending order
        # 如果用户是管理员，则按创建时间降序排序
        if 'admin' in user_roles:
            return query.order_by(self.model.create_time.desc())
        # Otherwise, filter by labels containing the current user's username and order by creation time
        # 否则，按包含当前用户用户名的标签过滤，并按创建时间排序
        return query.filter(
            or_(
                self.model.labels.contains('"%s"' % g.user.username),
            )
        ).order_by(self.model.create_time.desc())


# Base class for CRD ModelView
# CRD ModelView的基类
class Crd_ModelView_Base:
    # Columns to display in the list view
    # 列表视图中显示的列
    list_columns = ['name', 'namespace_url', 'create_time', 'status', 'username', 'stop']
    # Columns to display in the show view
    # 详细视图中显示的列
    show_columns = [
        'name',
        'namespace',
        'create_time',
        'status',
        'annotations_html',
        'labels_html',
        'spec_html',
        'status_more_html',
        'info_json_html',
    ]
    # Columns that can be used for ordering
    # 可用于排序的列
    order_columns = ['id']
    # Base permissions for the view
    # 视图的基本权限
    base_permissions = ['can_show', 'can_list', 'can_delete']
    # Commented out: alternative base permissions
    # 已注释：备用基本权限
    # base_permissions = ['list','delete','show']
    # Commented out: label columns configuration
    # 已注释：标签列配置
    # label_columns = {
    #     "annotations_html": _("Annotations"),
    #     "labels_html": _("Labels"),
    #     "name": _("名称"),
    #     "spec_html": _("Spec"),
    #     "status_more_html": _("Status"),
    #     "namespace_url":_("命名空间"),
    #     "create_time":_("创建时间"),
    #     "status": _("状态"),
    #     "username": _("关联用户"),
    #     "log": _("日志"),
    # }
    # Name of the Custom Resource Definition
    # 自定义资源定义的名称
    crd_name = ''
    # Default order for listing items
    # 列表项的默认排序
    base_order = ('create_time', 'desc')
    # List of base filters to apply
    # 要应用的基本过滤器列表
    base_filters = [['id', CRD_Filter, lambda: []]]

    # Method to list CRD objects from Kubernetes and store them in the database
    # 从Kubernetes列出CRD对象并将其存储在数据库中的方法
    def base_list(self):
        # Initialize Kubernetes client
        # 初始化Kubernetes客户端
        k8s_client = py_k8s.K8s()
        # Get CRD information from configuration
        # 从配置中获取CRD信息
        crd_info = conf.get('CRD_INFO', {}).get(self.crd_name, {})
        if crd_info:
            # Get all CRD objects across namespaces
            # 获取所有命名空间中的CRD对象
            crds = k8s_client.get_crd_all_namespaces(
                group=crd_info['group'], version=crd_info['version'], plural=crd_info['plural']
            )

            # Delete all existing records in the database (assuming IDs start from 0)
            # 删除数据库中所有现有记录（假设ID从0开始）
            db.session.query(self.datamodel.obj).delete()
            # Add new records to the database
            # 将新记录添加到数据库
            for crd in crds:
                try:
                    labels = json.loads(crd['labels'])
                    if 'run-rtx' in labels:
                        crd['username'] = labels['run-rtx']
                    elif 'upload-rtx' in labels:
                        crd['username'] = labels['upload-rtx']
                except Exception as e:
                    logging.error(e)
                crd_model = self.datamodel.obj(**crd)
                db.session.add(crd_model)

            # Commit changes to the database
            # 提交更改到数据库
            db.session.commit()

    # Base method for bulk deletion of CRD objects
    # 批量删除CRD对象的基础方法
    # @pysnooper.snoop()
    def base_muldelete(self, items):
        if not items:
            abort(404)
        for item in items:
            if item:
                try:
                    labels = json.loads(item.labels) if item.labels else {}
                    kubeconfig = None
                    # If 'pipeline-id' is in labels, get kubeconfig from the associated pipeline
                    # 如果'pipeline-id'在标签中，则从关联的管道获取kubeconfig
                    if 'pipeline-id' in labels:
                        pipeline = (
                            db.session.query(Pipeline)
                            .filter_by(id=int(labels['pipeline-id']))
                            .first()
                        )
                        if pipeline:
                            kubeconfig = pipeline.project.cluster.get('KUBECONFIG', '')

                    # Initialize Kubernetes client
                    # 初始化Kubernetes客户端
                    k8s_client = py_k8s.K8s(kubeconfig)
                    # Get CRD information
                    # 获取CRD信息
                    crd_info = conf.get('CRD_INFO', {}).get(self.crd_name, {})
                    if crd_info:
                        # Delete the CRD object from Kubernetes
                        # 从Kubernetes删除CRD对象
                        k8s_client.delete_crd(
                            group=crd_info['group'],
                            version=crd_info['version'],
                            plural=crd_info['plural'],
                            namespace=item.namespace,
                            name=item.name,
                        )
                        # Commented out: old logic for updating status in DB
                        # 已注释：更新数据库中状态的旧逻辑
                        # db_crds = db.session.query(self.datamodel.obj).filter(
                        # self.datamodel.obj.name.in_(crd_names)).all()
                        # for db_crd in db_crds:
                        #     db_crd.status = 'Deleted'
                        # db.session.commit()
                        # Update item status in DB
                        # 更新数据库中的项目状态
                        item.status = 'Deleted'
                        item.change_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        db.session.commit()
                        # Push notification message
                        # 推送通知消息
                        push_message(
                            conf.get('ADMIN_USER', '').split(','),
                            '手动触发stop %s %s' % (crd_info['plural'], item.name),
                        )

                except Exception as e:
                    flash(str(e), 'danger')

    # Pre-delete hook: called before deleting an item
    # 预删除钩子：在删除项目之前调用
    def pre_delete(self, item):
        # Perform base bulk deletion for the item
        # 对项目执行基础批量删除
        self.base_muldelete([item])
        # If ModelArts ID exists, stop the ModelArts job
        # 如果ModelArts ID存在，则停止ModelArts作业
        modelarts_id = item.get_modelarts_id()
        if modelarts_id:
            modelarts_job = ModelArtsJob()
            modelarts_job.stop_job(modelarts_id)

    # Pre-show hook: called before displaying item details
    # 预显示钩子：在显示项目详细信息之前调用
    def pre_show(self, item):
        if not item:
            return
        try:
            # Sync job status and create model if finished
            # 同步作业状态并在完成后创建模型
            training_job = sync_job_status(item.id)
            create_model_if_finished(training_job)
        except Exception:
            import traceback

            logging.error(traceback.format_exc())

    # Stop CRD object API endpoint
    # 停止CRD对象API端点
    @expose('/stop/<crd_id>')
    def stop(self, crd_id):
        # Query CRD object by ID
        # 按ID查询CRD对象
        crd = db.session.query(self.datamodel.obj).filter_by(id=crd_id).first()
        # Perform base bulk deletion for the CRD
        # 对CRD执行基础批量删除
        self.base_muldelete([crd])
        flash('清理完成', 'success')
        self.update_redirect()
        return redirect(self.get_redirect())

    # Action to stop all selected CRD objects
    # 停止所有选定CRD对象的操作
    @action('stop_all', __('Stop'), __('Stop all Really?'), 'fa-trash', single=False)
    def stop_all(self, items):
        # Perform base bulk deletion for selected items
        # 对选定项目执行基础批量删除
        self.base_muldelete(items)
        self.update_redirect()
        return redirect(self.get_redirect())

    # Commented out: list method override
    # 已注释：列表方法覆盖
    # @event_logger.log_this
    # @expose("/list/")
    # @has_access
    # def list(self):
    #     self.base_list()
    #     widgets = self._list()
    #     res = self.render_template(
    #         self.list_template, title=self.list_title, widgets=widgets
    #     )
    #     return res

    # Action for bulk deletion of CRD objects
    # 批量删除CRD对象的操作
    @action('muldelete', __('Delete'), __('Delete all Really?'), 'fa-trash', single=False)
    def muldelete(self, items):
        # Perform base bulk deletion for selected items
        # 对选定项目执行基础批量删除
        self.base_muldelete(items)
        for item in items:
            if item:
                # Call _delete method for each item
                # 对每个项目调用_delete方法
                self._delete(item.id)
        self.update_redirect()
        return redirect(self.get_redirect())


# Custom filter for Workflow objects
# Workflow对象的自定义过滤器
class Workflow_Filter(MyappFilter):
    # Apply the filter to the query
    # 将过滤器应用于查询
    # @pysnooper.snoop()
    def apply(self, query, func):
        # Get user roles and convert to lowercase
        # 获取用户角色并转换为小写
        user_roles = [role.name.lower() for role in list(self.get_user_roles())]
        # If user is an admin, order by creation time in descending order
        # 如果用户是管理员，则按创建时间降序排序
        if 'admin' in user_roles:
            return query.order_by(self.model.create_time.desc())
        # Otherwise, filter by username and order by creation time
        # 否则，按用户名过滤并按创建时间排序
        return query.filter(self.model.username == g.user.username).order_by(
            self.model.create_time.desc()
        )


# Base class for Workflow ModelView, listing running workflows
# Workflow ModelView的基类，列出正在运行的工作流
class Workflow_ModelView_Base(Crd_ModelView_Base):
    # Base filters for this view
    # 此视图的基本过滤器
    base_filters = [['id', Workflow_Filter, lambda: []]]

    # Static method to delete a workflow and its associated containers
    # 删除工作流及其关联容器的静态方法
    # @pysnooper.snoop()
    @staticmethod
    def delete_workflow(workflow):
        workflow.delete_k8s_workflow()

    # Stop workflow API endpoint
    # 停止工作流API端点
    @expose('/stop/<crd_id>', methods=['GET', 'POST'])
    @wrap_response
    def stop(self, crd_id):
        # Query workflow by ID
        # 按ID查询工作流
        workflow = db.session.query(self.datamodel.obj).filter_by(id=crd_id).first()
        if not workflow:
            raise BizError(CommonErrorCode.NOT_FOUND.value, '任务不存在，刷新后重试')

        # Check workflow status and perform action accordingly
        # 检查工作流状态并相应执行操作
        if workflow.display_status in ['Failed', 'ERROR']:
            raise BizError(0, '任务已失败')
        elif workflow.display_status in ['Succeeded', 'Completed']:
            raise BizError(0, '任务已成功')
        elif workflow.display_status in ['Terminated', 'Stopped']:
            raise BizError(0, '任务已停止')
        elif workflow.display_status in ['Creating', 'Pending', 'Running']:
            workflow.stop_workflow()
        else:
            raise BizError(1000, '未知状态')

        conf.get('MODEL_URLS', {}).get('workflow', '')
        # return redirect(url)
        return workflow.to_json()

    # Restart workflow API endpoint
    # 重启工作流API端点
    @expose('/restart/<crd_id>', methods=['POST'])
    @wrap_response
    def restart(self, crd_id):
        # Query workflow by ID
        # 按ID查询工作流
        workflow = db.session.query(self.datamodel.obj).filter_by(id=crd_id).first()
        if not workflow:
            raise BizError(CommonErrorCode.NOT_FOUND.value, '任务不存在，刷新后重试')
        # Check if continuing training is enabled
        # 检查是否启用了继续训练
        continuing_training = json.loads(workflow.algorithm).get('continuing_training', False)
        if not continuing_training:
            raise BizError(CommonErrorCode.NOT_FOUND.value, '任务不支持断点续训')
        # Check workflow status and perform restart logic
        # 检查工作流状态并执行重启逻辑
        if workflow.display_status in ['Terminated', 'Stopped']:
            restart_training(workflow, db.session)
        elif workflow.display_status in ['Failed', 'ERROR']:
            error_code = json.loads(workflow.status_more).get('error_code', '')
            if can_continuing_training(error_code):
                restart_training(workflow, db.session)
            else:
                raise BizError(CommonErrorCode.NOT_FOUND.value, '当前错误不支持断点续训')
        else:
            raise BizError(1000, f'当前状态{workflow.display_status}不能重新运行')

        return workflow.to_json()

    # Title for the view
    # 视图的标题
    label_title = '运行实例'
    # Data model interface for Workflow
    # Workflow的数据模型接口
    datamodel = SQLAInterface(Workflow)
    # Columns to display in the list view
    # 列表视图中显示的列
    list_columns = [
        'project',
        'display_name',
        'create_time',
        'change_time',
        'elapsed_time',
        'final_status',
        'status',
        'display_status',
        'username',
        'stop',
        'display_engine',
        'flavor_desc',
        'status_message',
    ]
    # Columns that can be used for searching
    # 可用于搜索的列
    search_columns = [
        'status',
        'display_status',
        'labels',
        'name',
        'display_name',
        'annotations',
        'spec',
        'status_more',
        'username',
        'create_time',
    ]
    # Column widths for display
    # 显示的列宽
    cols_width = {
        'project': {'type': 'ellip2', 'width': 200},
        'create_time': {'type': 'ellip2', 'width': 200},
        'change_time': {'type': 'ellip2', 'width': 200},
    }
    # Columns to display in the show view
    # 详细视图中显示的列
    show_columns = [
        'name',
        'display_name',
        'namespace',
        'create_time',
        'status',
        'display_status',
        'task_status',
        'annotations_html',
        'labels_html',
        'spec_html',
        'status_more_html',
        'info_json_html',
        'algorithm_json',
        'status_message',
        'elapsed_time',
    ]
    # Name of the Custom Resource Definition
    # 自定义资源定义的名称
    crd_name = 'workflow'


# Workflow ModelView class, inheriting from base and including DeleteMixin
# Workflow ModelView类，继承自基类并包含DeleteMixin
class Workflow_ModelView(Workflow_ModelView_Base, MyappModelView, DeleteMixin):
    # Data model interface for Workflow
    # Workflow的数据模型接口
    datamodel = SQLAInterface(Workflow)


# Add the Workflow_ModelView to the appbuilder without a menu entry
# 将Workflow_ModelView添加到appbuilder，不带菜单项
appbuilder.add_view_no_menu(Workflow_ModelView)


# Workflow ModelView API class, inheriting from base and MyappModelRestApi
# Workflow ModelView API类，继承自基类和MyappModelRestApi
class Workflow_ModelView_Api(Workflow_ModelView_Base, MyappModelRestApi):
    # Data model interface for Workflow
    # Workflow的数据模型接口
    datamodel = SQLAInterface(Workflow)
    # Route base for the API endpoint
    # API端点的路由基础
    route_base = '/workflow_modelview/api'


# Add the Workflow_ModelView_Api to the appbuilder
# 将Workflow_ModelView_Api添加到appbuilder
appbuilder.add_api(Workflow_ModelView_Api)


# Commented out: Tfjob ModelView classes
# 已注释：Tfjob ModelView类
#
#
# # list正在运行的tfjob
# class Tfjob_ModelView(Crd_ModelView_Base,MyappModelView,DeleteMixin):
#     label_title = 'tf分布式任务'
#     datamodel = SQLAInterface(Tfjob)
#     crd_name = 'tfjob'
#     list_columns = ['name','pipeline_url','run_instance','namespace_url','create_time','status',
# 'username','stop']
#
#
# appbuilder.add_view(Tfjob_ModelView,"TFjob",href="/tfjob_modelview/list/?_flt_2_name=",
# icon = 'fa-tasks',category = '训练')
# # 添加api
# class Tfjob_ModelView_Api(Crd_ModelView_Base,MyappModelRestApi):
#     label_title = 'tf分布式任务'
#     datamodel = SQLAInterface(Tfjob)
#     route_base = '/tfjob_modelview/api'
#     crd_name = 'tfjob'
#
# appbuilder.add_api(Tfjob_ModelView_Api)
#
#
# # list正在运行的xgb
# class Xgbjob_ModelView(Crd_ModelView_Base,MyappModelView,DeleteMixin):
#     label_title = 'xgb分布式任务'
#     datamodel = SQLAInterface(Xgbjob)
#     crd_name = 'xgbjob'
#
# appbuilder.add_view(Xgbjob_ModelView,"XGBjob",href="/xgbjob_modelview/list/?_flt_2_name=",
# icon = 'fa-tasks',category = '训练')
#
#
# # 添加api
# class Xgbjob_ModelView_Api(Crd_ModelView_Base,MyappModelRestApi):
#     label_title = 'xgb分布式任务'
#     datamodel = SQLAInterface(Xgbjob)
#     route_base = '/xgbjob_modelview/api'
#     crd_name = 'xgbjob'
#
# appbuilder.add_api(Xgbjob_ModelView_Api)
#
#
# # list正在运行的pytorch
# class Pytorchjob_ModelView(Crd_ModelView_Base,MyappModelView,DeleteMixin):
#     label_title = 'pytorch分布式任务'
#     datamodel = SQLAInterface(Pytorchjob)
#     crd_name = 'pytorchjob'
#
# appbuilder.add_view(Pytorchjob_ModelView,"Pytorchjob",href="/pytorchjob_modelview/list/
# ?_flt_2_name=",icon = 'fa-tasks',category = '训练')
#
#
# # 添加api
# class Pytorchjob_ModelView_Api(Crd_ModelView_Base,MyappModelRestApi):
#     label_title = 'pytorch分布式任务'
#     datamodel = SQLAInterface(Pytorchjob)
#     route_base = '/pytorchjob_modelview/api'
#     crd_name = 'pytorchjob'
#
# appbuilder.add_api(Pytorchjob_ModelView_Api)